home *** CD-ROM | disk | FTP | other *** search
/ Clickx 65 / Clickx 65.iso / software / internet / xmarks / xmarks-3.1.1.xpi / components / foxmarks-service.js next >
Encoding:
JavaScript  |  2009-05-05  |  36.2 KB  |  1,141 lines

  1. /*
  2.  Copyright 2005-2008 Foxmarks Inc.
  3.  
  4.  foxmarks-service.js: component that implements the "service" interface to
  5.  the core synchronization code.
  6.  
  7.  */
  8.  
  9. var Cc = Components.classes;
  10. var Ci = Components.interfaces;
  11.  
  12. function LoadJavascript(filename, id) {
  13.     if (id == "undefined") {
  14.         Cc["@mozilla.org/moz/jssubscript-loader;1"].
  15.         getService(Ci.mozIJSSubScriptLoader).
  16.         loadSubScript(filename, null);
  17.     }
  18. }
  19.  
  20. function GetTopWin(wintype) {
  21.     var topwin = Cc['@mozilla.org/appshell/window-mediator;1'].
  22.         getService(Ci.nsIWindowMediator).
  23.         getMostRecentWindow(wintype);
  24.  
  25.     return topwin;
  26. }
  27.  
  28. function FoxmarksSetCookie() {
  29.     var url = "http://www.xmarks.com";
  30.     var d = new Date();
  31.     d.setTime(d.getTime() + (20 * 365 * 24 * 60 * 60 * 1000));
  32.     var cm = Components.classes["@mozilla.org/cookiemanager;1"].
  33.                     getService(Components.interfaces.nsICookieManager2);
  34.  
  35.     cm.add(".xmarks.com", "/", "mid", gSettings.machineId, false, false, false,
  36.             d.getTime() / 1000);
  37. }
  38.  
  39. function FoxmarksLaunchUpgradePage() {
  40.     FoxmarksSetCookie();
  41.  
  42.     upgradeCallback.timer = Cc["@mozilla.org/timer;1"].
  43.         createInstance(Ci.nsITimer);
  44.     upgradeCallback.timer.initWithCallback(upgradeCallback, 5000,
  45.         Ci.nsITimer.TYPE_ONE_SHOT);
  46. }
  47.  
  48. function FoxmarksBuildPostData(num_bookmarks){
  49.     return {
  50.         username: gSettings.username,
  51.         version: FoxmarksVersion(),
  52.         types: {
  53.             bookmarks: {
  54.                 is_enabled: gSettings.isSyncEnabled("bookmarks"),
  55.                 count: num_bookmarks
  56.             },
  57.             passwords: {
  58.                 is_enabled: gSettings.isSyncEnabled("passwords")
  59.             }
  60.         }
  61.     };
  62. }
  63. var upgradeCallback = {
  64.     notify: function(timer) {
  65.         FoxmarksSyncService.server.countItems("bookmarks",
  66.             "bookmark", function(status, num_bookmarks){
  67.             var currver = FoxmarksVersion();
  68.             var wm = Cc['@mozilla.org/appshell/window-mediator;1'].
  69.                 getService(Ci.nsIWindowMediator);
  70.             var topWindow = wm.getMostRecentWindow('navigator:browser');
  71.  
  72.             if(topWindow &&
  73.                 topWindow.document){ 
  74.                 var appcontent =
  75.                     topWindow.document.getElementById("appcontent");
  76.                 var listener = function(e){
  77.                     var doc = e.originalTarget;
  78.                     var button = 
  79.                         doc.getElementById(
  80.                         "set_up_password_sync_button"
  81.                     );
  82.  
  83.                     if(button){
  84.                         button.setAttribute("onclick",  null);
  85.                         button.addEventListener(
  86.                             "click",
  87.                             function(){
  88.                                 var os = Cc["@mozilla.org/observer-service;1"].
  89.                                     getService(Ci.nsIObserverService);
  90.                                 os.notifyObservers(null, 
  91.                                     "foxmarks-showsettingpane", 
  92.                                     "foxmarks-syncpane"
  93.                                 );
  94.                             }, 
  95.                             true
  96.                         );
  97.                         appcontent.removeEventListener(
  98.                             "DOMContentLoaded",
  99.                             listener, 
  100.                             false
  101.                         );
  102.                     }
  103.                 };
  104.                 if(appcontent){
  105.                     appcontent.addEventListener(
  106.                         "DOMContentLoaded",
  107.                         listener, 
  108.                         false
  109.                     );
  110.                 }
  111.             }
  112.             FoxmarksOpenInNewTab(gSettings.httpProtocol + gSettings.webHost + "/firefox/upgrade/" + currver, 
  113.                 true, FoxmarksBuildPostData(num_bookmarks));
  114.             gSettings.currVersion = currver;
  115.         });
  116.     }
  117. }
  118. function FoxmarksCheckForServerChanges() {
  119.         var url = gSettings.httpProtocol + gSettings.apiHost +
  120.             "/internal/serverprefs";
  121.         var request = new Request(
  122.             "POST", 
  123.             url,
  124.             {
  125.                 mid: gSettings.machineId,
  126.                 cid: "xmfx"
  127.             },
  128.             false,
  129.             null, 
  130.             false, 
  131.             true
  132.         );
  133.         request.Start(function(response){
  134.             if(response && response.status === 0){
  135.                 if(response.prefs && 
  136.                     response.version > gSettings.serverVersion){
  137.                     var prefs = response.prefs;
  138.                     for(var pref in prefs){
  139.                         if(prefs.hasOwnProperty(pref)){
  140.                             gSettings[pref] = prefs[pref];
  141.                         }
  142.                     }
  143.                     gSettings.serverVersion = response.version;
  144.                 }
  145.             }
  146.             var os = Cc["@mozilla.org/observer-service;1"]
  147.                 .getService(Ci.nsIObserverService);
  148.             os.notifyObservers(null, "foxmarks-realstart", "");
  149.         });
  150. }
  151.  
  152. function FoxmarksLaunchSetupWizard() {
  153.     FoxmarksSetCookie();
  154.     FoxmarksWizardCallback.timer = Cc["@mozilla.org/timer;1"].
  155.         createInstance(Ci.nsITimer);
  156.     FoxmarksWizardCallback.timer.initWithCallback(FoxmarksWizardCallback, 5000,
  157.         Ci.nsITimer.TYPE_ONE_SHOT);
  158. }
  159.  
  160. var FoxmarksWizardCallback = {
  161.     notify: function(timer) {
  162.         var url = "http://" + gSettings.staticHost + gSettings.wizardPrefs;
  163.         var request = new Request(
  164.             "GET", 
  165.             url,
  166.             null,
  167.             false,
  168.             null, 
  169.             false, 
  170.             true
  171.         );
  172.         request.Start(function(response){
  173.             var os = Cc["@mozilla.org/observer-service;1"]
  174.                 .getService(Ci.nsIObserverService);
  175.             var JSON = Cc["@mozilla.org/dom/json;1"].
  176.                 createInstance(Ci.nsIJSON);
  177.             os.notifyObservers(null, "foxmarks-newpopup", JSON.encode(response));
  178.         });
  179.     }
  180. }
  181.  
  182. function HandleShutdown(cancel) {
  183.     var retval = { helpurl: null };
  184.     var sb = Bundle().GetStringFromName;
  185.     var dontask = {value: false};
  186.     var rv = 0;
  187.  
  188.     if (!gSettings.haveSynced || GetState() != "dirty") {
  189.         return;
  190.     }
  191.  
  192.     var topwin = GetTopWin();
  193.  
  194.     if (!topwin) {
  195.         LogWrite("HandleShutdown: Couldn't find a topwin!");
  196.         return;
  197.     }
  198.  
  199.     if (gSettings.syncOnShutdown && gSettings.syncOnShutdownAsk) {
  200.  
  201.         rv = Cc["@mozilla.org/embedcomp/prompt-service;1"].
  202.         getService(Ci.nsIPromptService).
  203.         confirmEx(topwin, sb("appname.long"), sb("msg.unsynced"),
  204.             Ci.nsIPromptService.STD_YES_NO_BUTTONS, null, null, null,
  205.             sb("msg.dontask"), dontask);
  206.         // Reverse sense: confirmEx returns 0 - yes, 1 - no
  207.         rv = !rv;
  208.  
  209.         // If user says "don't ask me again", set syncOnShutdown to whatever
  210.         // they have chosen in this instance.
  211.         if (dontask.value) {
  212.             gSettings.syncOnShutdown = rv;
  213.         }
  214.         gSettings.syncOnShutdownAsk = !dontask.value;
  215.  
  216.     } else {                           // don't ask
  217.         rv = gSettings.syncOnShutdown;
  218.     }
  219.  
  220.     if (rv) {
  221.         var win = topwin.openDialog(
  222.             "chrome://foxmarks/content/foxmarks-progress.xul", "_blank",
  223.             "chrome,dialog,modal,centerscreen", "synch", retval, null);
  224.         if (retval.helpurl) { // we hit an error and user pressed help button
  225.             if (cancel instanceof Ci.nsISupportsPRBoolean) {
  226.                 cancel.value = true;
  227.                 topwin.openDialog("chrome://browser/content/browser.xul",
  228.                     "_blank", "chrome,all,dialog=no", retval.helpurl);
  229.             }
  230.         }
  231.     }
  232. }
  233.  
  234. function LoadFiles() {
  235.     LoadJavascript("chrome://foxmarks/content/foxmarks-log.js",
  236.         typeof(LogWrite));
  237.     LoadJavascript("chrome://foxmarks/content/foxmarks-settings.js",
  238.         typeof(gSettings));
  239.     LoadJavascript("chrome://foxmarks/content/foxmarks-update.js",
  240.         typeof(ForceUpdate));
  241.     LoadJavascript("chrome://foxmarks/content/foxmarks-clobber.js",
  242.         typeof(onClobberCancel));
  243.     LoadJavascript("chrome://foxmarks/content/foxmarks-bookmark.js",
  244.         typeof(loadDatasourceSet));
  245.     if ("@mozilla.org/browser/nav-bookmarks-service;1" in Cc)
  246.         LoadJavascript("chrome://foxmarks/content/foxmarks-places.js",
  247.             typeof(BookmarkDatasource));
  248.     else
  249.         LoadJavascript("chrome://foxmarks/content/foxmarks-rdf.js",
  250.             typeof(BookmarkDatasource));
  251.     LoadJavascript("chrome://foxmarks/content/foxmarks-nodes.js",
  252.         typeof(Node));
  253.     LoadJavascript("chrome://foxmarks/content/foxmarks-command.js",
  254.         typeof(Command));
  255.     LoadJavascript("chrome://foxmarks/content/foxmarks-core.js",
  256.         typeof(Synchronize));
  257.     LoadJavascript("chrome://foxmarks/content/foxmarks-network.js",
  258.         typeof(Request));
  259.     LoadJavascript("chrome://foxmarks/content/foxmarks-json.js",
  260.         "undefined");
  261.  
  262.     LoadJavascript("chrome://foxmarks/content/shared/Base64.js",
  263.         typeof(Base64));
  264.     LoadJavascript("chrome://foxmarks/content/shared/CreateAESManager.js",
  265.         typeof(CreateAESManager));
  266.     LoadJavascript("chrome://foxmarks/content/foxmarks-utils.js",
  267.         typeof(forEach));
  268.     LoadJavascript("chrome://foxmarks/content/foxmarks-unittest.js",
  269.         typeof(gFoxmarksUT));
  270.     LoadJavascript("chrome://foxmarks/content/foxmarks-uitools.js",
  271.         typeof(FoxmarksOpenWindowByType));
  272.     if("@mozilla.org/login-manager;1" in Cc){
  273.         LoadJavascript("chrome://foxmarks/content/foxmarks-password.js",
  274.             typeof(PasswordDatasource));
  275.     }
  276.     LoadJavascript("chrome://foxmarks/content/foxmarks-server.js",
  277.         typeof(SyncServer));
  278. }
  279.  
  280. var logStream = null;
  281.  
  282. function removeTempLogFile(){
  283.     var fileremoved = Cc['@mozilla.org/file/directory_service;1']
  284.         .getService(Ci.nsIProperties)
  285.         .get('ProfD', Ci.nsIFile);
  286.     fileremoved.append("xmarks.temp.log");
  287.     try {
  288.         fileremoved.remove(false);
  289.     } catch(e){}
  290. }
  291. function logMoveFile(){
  292.     try {
  293.         var file = Cc['@mozilla.org/file/directory_service;1']
  294.             .getService(Ci.nsIProperties)
  295.             .get('ProfD', Ci.nsIFile);
  296.         file.append("xmarks.log");
  297.  
  298.         var dir = Cc['@mozilla.org/file/directory_service;1']
  299.             .getService(Ci.nsIProperties)
  300.             .get('ProfD', Ci.nsIFile);
  301.         removeTempLogFile();
  302.         file.moveTo(dir, "xmarks.temp.log");
  303.  
  304.  
  305.         var fromstream = Cc["@mozilla.org/network/file-input-stream;1"]
  306.             .createInstance(Ci.nsIFileInputStream);
  307.         var tostream = Cc["@mozilla.org/network/file-output-stream;1"]
  308.             .createInstance(Ci.nsIFileOutputStream);
  309.  
  310.         fromstream.init(file, -1, 0x01, 0);
  311.         var logSeek = fromstream.QueryInterface(Ci.nsISeekableStream);
  312.         var lread = fromstream.QueryInterface(Ci.nsILineInputStream);
  313.         var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  314.             .createInstance(Ci.nsIScriptableUnicodeConverter);
  315.         converter.charset = "UTF-8";
  316.         var i =  100 * 1024;
  317.         if(i > file.fileSize){
  318.             i = file.fileSize;
  319.         }
  320.  
  321.         var filenew = Cc['@mozilla.org/file/directory_service;1']
  322.             .getService(Ci.nsIProperties)
  323.             .get('ProfD', Ci.nsIFile);
  324.         filenew.append("xmarks.log");
  325.         tostream.init(filenew, 0x02 | 0x08 | 0x10, 0664, 0);
  326.  
  327.         logSeek.seek(logSeek.NS_SEEK_END, -i);
  328.  
  329.         var buf;
  330.         var cont = true;
  331.         var lineData = {};
  332.         var ctr = 0;
  333.         // throw out the first one; could be mid line
  334.         cont = lread.readLine(lineData);
  335.         while(cont){
  336.             lineData = {};
  337.             cont = lread.readLine(lineData);
  338.             if(cont){
  339.                 buf = converter.ConvertToUnicode(lineData.value) + "\n";
  340.                 tostream.write(buf, buf.length);
  341.             }
  342.         }
  343.  
  344.      } catch(e){
  345.         Components.utils.reportError(e);
  346.      } finally {
  347.         if(fromstream !== undefined)
  348.             fromstream.close();
  349.         if(tostream !== undefined)
  350.             tostream.close();
  351.      }
  352.     removeTempLogFile();
  353. }
  354. function logFileOpen() {
  355.     var file = Cc['@mozilla.org/file/directory_service;1']
  356.     .getService(Ci.nsIProperties)
  357.     .get('ProfD', Ci.nsIFile);
  358.  
  359.     var needsTruncate = false;
  360.     var filesize = 0;
  361.     file.append("xmarks.log");
  362.  
  363.     // check the file size
  364.     try {
  365.        if(file.isFile()){
  366.             filesize = file.fileSize;
  367.        }
  368.  
  369.        if(filesize > 500 * 1024 && gSettings.truncateLog ){
  370.             logMoveFile();
  371.        }
  372.     } catch(e) {
  373.    //     Components.utils.reportError(e);
  374.     }
  375.     
  376.     try {
  377.         logStream = Cc["@mozilla.org/network/file-output-stream;1"]
  378.         .createInstance(Ci.nsIFileOutputStream);
  379.  
  380.         // use write, append, create
  381.         logStream.init(file, 0x02 | 0x08 | 0x10, 0664, 0);
  382.     } catch (e) {
  383.         // We failed to open. Close and try again next time.
  384.         logFileClose();
  385.     }
  386. }
  387.  
  388. function logFileClose() {
  389.     try {
  390.         logStream.close();
  391.     } catch (e) {}
  392.     logStream = null;
  393. }
  394.  
  395. var gFailureCount = 0;// number of consecutive times we've failed
  396. var gBackoffUntil = 0;// if we've been failing, our backoff time (ms since 1970)
  397. var gServerBackoff = 
  398.     { 'bookmarks': 0,
  399.       'passwords': 0,
  400.       min: function(){
  401.         return Math.min(this['bookmarks'], this['passwords']);
  402.       },
  403.       max: function(){
  404.         return Math.max(this['bookmarks'], this['passwords']);
  405.       },
  406.       clear: function(){
  407.         this['bookmarks'] = 0;
  408.         this['passwords'] = 0;
  409.       }
  410.     };  // seconds server wants us to back off
  411.  
  412. function ReturnErrorMsg(code, msg, restoreState, noBackoff) {
  413.     gSettings.lastError  = code;
  414.     gSettings.lastError  = code;
  415.     Notify({status: code, msg: msg });
  416.     ClearBusy();
  417.     SetState(restoreState ? restoreState : ((code == 503 || code == 2)
  418.             ? "dirty" : "error"));
  419.     LogWrite("Returned error: " + msg + "(" + code + ")");
  420.     if(code != 2 && !noBackoff){
  421.         var d = new Date();
  422.         // Initial back-off is 15 minutes, doubling with each error
  423.         gBackoffUntil = d.getTime() + 1000 * (Math.max(
  424.             15 * 60 * Math.pow(2, gFailureCount++),
  425.             gServerBackoff.max()) + Math.floor(Math.random() * 15));
  426.         gServerBackoff.clear();;
  427.         var retry = new Date();
  428.         retry.setTime(gBackoffUntil);
  429.         LogWrite("Will retry at " + retry);
  430.     }
  431.     FoxmarksSyncService.lastError = code;
  432. }
  433.  
  434. function ReturnErrorCode(code) {
  435.     ReturnErrorMsg(code, MapError(code));
  436. }
  437.  
  438. function ReturnSuccess(msgname, args, restoreState) {
  439.     if (args == null) {
  440.         var args = {};
  441.     }
  442.  
  443.     args.status = 0;
  444.     args.msg = Bundle().GetStringFromName(msgname);
  445.  
  446.     SetState(restoreState ? restoreState : "ready");
  447.     ClearBusy();
  448.     gFailureCount = 0;
  449.     FoxmarksSyncService.lastError = 0;
  450.     LogWrite("Success: " + Bundle().GetStringFromName(msgname));
  451.     Notify(args);
  452. }
  453.  
  454. function SetBusy() {
  455.     if (IsBusy._busy) {
  456.         return false;
  457.     } else {
  458.         IsBusy._busy = true;
  459.         SetState("working");
  460.         return true;
  461.     }
  462. }
  463.  
  464. function ClearBusy() {
  465.     IsBusy._busy = false;
  466. }
  467.  
  468. function IsBusy() {
  469.     return IsBusy._busy;
  470. }
  471. IsBusy._busy = false;
  472.  
  473. function GetState() {
  474.     return GetState._state;
  475. }
  476. GetState._state = "ready";
  477.  
  478. function SetState(newstate) {
  479.     if (newstate == GetState()) {
  480.         return;
  481.     }
  482.  
  483.     var os = Cc["@mozilla.org/observer-service;1"]
  484.     .getService(Ci.nsIObserverService);
  485.  
  486.     os.notifyObservers(null, "foxmarks-statechange", newstate);
  487.  
  488.     GetState._state = newstate;
  489. }
  490.  
  491. // Internal callbacks
  492.  
  493. function LogStart(op, dest) {
  494.     LogWrite("------ Xmarks/" + FoxmarksVersion() + " (" + 
  495.             FoxmarksSyncService.getStorageEngine("bookmarks") + ") starting " + op +
  496.             " with " + dest + " ------");
  497. }
  498.  
  499. ///////////////////////////////////////////////////////////////////////////
  500. //
  501. // nsFoxmarksService
  502. //
  503.  
  504. var FoxmarksSyncService = null;  // set during initialization to instance object
  505.  
  506. function nsFoxmarksService() {
  507.     LoadFiles();
  508. }
  509.  
  510. nsFoxmarksService.prototype = {
  511.     /////////////////////////////////////////////////////////////////////////
  512.     // nsIFoxmarksService
  513.  
  514.     timer: null,
  515.     _server: null,
  516.     lastmodified: null,
  517.  
  518.     status: function(syncType) {
  519.         // return if we're currently processing another request
  520.         if (!SetBusy()) {
  521.             return false;
  522.         }
  523.         LogStart("status", gSettings.host);
  524.         if(syncType === undefined)
  525.             syncType = "bookmarks";
  526.  
  527.         this.server.status(syncType, function(status, response){
  528.             if (status) {
  529.                 ReturnErrorCode(status);
  530.             }
  531.             else {
  532.                 ReturnSuccess("msg.accountverified", response);
  533.             }
  534.         });
  535.         return true;
  536.     },
  537.     extstatus: function(syncType) {
  538.         // return if we're currently processing another request
  539.         if (!SetBusy()) {
  540.             return false;
  541.         }
  542.         LogStart("extstatus", gSettings.host);
  543.         if(syncType === undefined)
  544.             syncType = "bookmarks";
  545.  
  546.         this.server.extstatus(syncType, function(status, response){
  547.             if (status) {
  548.                 ReturnErrorCode(status);
  549.             }
  550.             else {
  551.                 ReturnSuccess("msg.accountverified", response);
  552.             }
  553.         });
  554.         return true;
  555.     },
  556.     purgepasswords: function(){
  557.         LogStart("purgepasswords", gSettings.host);
  558.         this.server.purgepasswords(function(status){
  559.             if (!status) {
  560.                 ReturnSuccess("msg.synccompleted");
  561.             }
  562.             else {
  563.                 ReturnErrorCode(status);
  564.             }
  565.         });
  566.         return true;
  567.  
  568.     },
  569.  
  570.     verifypin: function(pin) {
  571.         // return if we're currently processing another request
  572.         if (!SetBusy()) {
  573.             return false;
  574.         }
  575.         LogStart("status", gSettings.host);
  576.  
  577.         this.server.verifypin(pin, function(status, response){
  578.             if (status) {
  579.                 ReturnErrorCode(status);
  580.             }
  581.             else {
  582.                 ReturnSuccess("msg.pinverified", response);
  583.             }
  584.         });
  585.         return true;
  586.     },
  587.  
  588.     synchronize: function(automatic) {
  589.         var prevState = GetState();
  590.  
  591.         // return if we're currently processing another request
  592.         if (!SetBusy()) {
  593.             return false;
  594.         }
  595.  
  596.         LogStart("sync", gSettings.host);
  597.         this.server.manual = automatic === true ? false : true;
  598.         this.server.sync(prevState, function(status){
  599.             if (!status) {
  600.                 ReturnSuccess("msg.synccompleted");
  601.             }
  602.             else {
  603.                 ReturnErrorCode(status);
  604.             }
  605.         });
  606.         return true;
  607.     },
  608.  
  609.     synchronizeInitial: function (remoteIsMaster, doMerge) {
  610.         // return if we're currently processing another request
  611.         if (!SetBusy()) {
  612.             return false;
  613.         }
  614.  
  615.         LogStart("initial sync", gSettings.host);
  616.         this.server.manual = true;
  617.  
  618.         // We need to self correct for password sync.  If there is an
  619.         // uploadReq == true, we know it doesn't have data on the server.
  620.         // if it's false, we should force a merge per our PRD.
  621.         if(gSettings.isSyncEnabled("passwords")){
  622.             // Set default higher if we're doing password sync
  623.             gSettings.securityLevel = 1;
  624.             if(!gSettings.mustUpload("passwords")){
  625.                 gSettings.setMustMerge("passwords", true);
  626.             }
  627.         }
  628.  
  629.         if (doMerge) {
  630.             this.server.merge(!remoteIsMaster, Finished);
  631.         } else {
  632.             if (remoteIsMaster) {
  633.                 this.server.download(Finished);
  634.             } else {
  635.                 this.server.upload(Finished);
  636.             }
  637.         }
  638.  
  639.         return true;
  640.  
  641.         function Finished(status) {
  642.             if (!status) {
  643.                 ReturnSuccess("msg.synccompleted");
  644.             } else {
  645.                 ReturnErrorCode(status);
  646.             }
  647.         }
  648.     },
  649.  
  650.     upload: function () {
  651.         // return if we're currently processing another request
  652.         if (!SetBusy()) {
  653.             return false;
  654.         }
  655.  
  656.         LogStart("upload", gSettings.host);
  657.  
  658.         this.server.manual = true;
  659.         this.server.upload(function(status){
  660.             if (!status) {
  661.                 ReturnSuccess("msg.uploadcompleted");
  662.             }
  663.             else {
  664.                 ReturnErrorCode(status);
  665.             }
  666.         });
  667.         return true;
  668.     },
  669.  
  670.     download: function () {
  671.         // return if we're currently processing another request
  672.         if (!SetBusy()) {
  673.             return false;
  674.         }
  675.  
  676.         LogStart("download", gSettings.host);
  677.  
  678.         this.server.manual = true;
  679.         this.server.download(function(status){
  680.             if (!status) {
  681.                 ReturnSuccess("msg.remotefilecopied");
  682.             }
  683.             else {
  684.                 ReturnErrorCode(status);
  685.             }
  686.         });
  687.         return true;
  688.     },
  689.  
  690.     _handleDataResponse: function(response){
  691.         var os = Cc["@mozilla.org/observer-service;1"]
  692.             .getService(Ci.nsIObserverService);
  693.  
  694.         os.notifyObservers(null, "foxmarks-dataservice", response.toSource());
  695.     },
  696.     getSimilarSites: function(url) {
  697.         LogStart("getSimilarSites", gSettings.turboTagHost);
  698.         this.server.getSimilarSites(url, this._handleDataResponse);
  699.         return true;
  700.     },
  701.     getTurboTags: function(url) {
  702.         LogStart("getTurboTags", gSettings.turboTagHost);
  703.         this.server.getTurboTags(url, this._handleDataResponse);
  704.         return true;
  705.     },
  706.     getProfileNames: function() {
  707.         var prevState = GetState();
  708.  
  709.         var funcFinished = function(status, response) {
  710.             if (!status) {
  711.                 LogWrite("GetProfileNames succeeded; response is " +
  712.                         response.toSource());
  713.                 ReturnSuccess("error.0", response, prevState);
  714.             } else {
  715.                 ReturnErrorMsg(status, MapError(status), prevState);
  716.             }
  717.         };
  718.         // return if we're currently processing another request
  719.         if (!SetBusy()) {
  720.             return false;
  721.         }
  722.  
  723.         LogStart("getProfileNames", gSettings.acctMgrHost);
  724.  
  725.         if (this.server.getProfileNames) {
  726.             this.server.getProfileNames(funcFinished);
  727.             return true;
  728.         } else {
  729.             return false;
  730.         }
  731.  
  732.     },
  733.  
  734.     launchSuccessPage: function(){
  735.         this.server.countItems("bookmarks",
  736.             "bookmark", function(status, num_bookmarks){
  737.             var currver = FoxmarksVersion();
  738.             FoxmarksOpenInNewTab(gSettings.httpProtocol +
  739.                 gSettings.webHost + "/firefox/success/" + currver, 
  740.                 true, FoxmarksBuildPostData(num_bookmarks));
  741.             gSettings.currVersion = currver;
  742.         });
  743.     },
  744.     cancel: function() {
  745.         this.server.cancel();
  746.     },
  747.     datacancel: function() {
  748.         this.server.datacancel();
  749.     },
  750.  
  751.     logWrite: function (msg) {
  752.         if (!logStream)
  753.             logFileOpen();
  754.  
  755.         var d = new Date();
  756.         var year = d.getFullYear();
  757.         var month = d.getMonth() + 1; if (month < 10) month = "0" + month;
  758.         var day = d.getDate(); if (day < 10) day = "0" + day;
  759.         var hour = d.getHours(); if (hour < 10) hour = "0" + hour;
  760.         var minute = d.getMinutes(); if (minute < 10) minute = "0" + minute;
  761.         var sec = d.getSeconds(); if (sec < 10) sec = "0" + sec;
  762.  
  763.         // format is [YYYY-MM-DD HH:MM:SS] msg\n
  764.         var string = "[" + year + "-" + month + "-" + day + " " + hour + ":" +
  765.             minute + ":" + sec + "] " + msg + "\n";
  766.  
  767.         if(gSettings.getDebugOption("dumplog"))
  768.             dump("Xmarks: " + string + "\n");
  769.         logStream.write(string, string.length)
  770.     },
  771.  
  772.     getState: function() {
  773.         return GetState();
  774.     },
  775.  
  776.     _password: null,
  777.     _pin: null,
  778.  
  779.     getPassword: function() {
  780.         return this._password;
  781.     },
  782.  
  783.     setPassword: function(password) {
  784.         this._password = password;
  785.     },
  786.     getPin: function() {
  787.         return this._pin;
  788.     },
  789.  
  790.     setPin: function(password) {
  791.         this._pin = password;
  792.     },
  793.  
  794.     getStorageEngine: function(synctype) {
  795.         return getDatasourceAttribute(synctype, "engine");
  796.     },
  797.  
  798.     getLastError: function() {
  799.         return this.lastError;
  800.     },
  801.  
  802.     lastError: 0,
  803.     _uninstall: false,
  804.     _channel: null,
  805.     _pingListener: {
  806.          onStartRequest: function () {},
  807.          onDataAvailable: function () {},
  808.          onStopRequest: function () {},
  809.          onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
  810.             this._newchannel = aNewChannel;
  811.          },
  812.         getInterface: function (aIID) {
  813.             try {
  814.                 return this.QueryInterface(aIID);
  815.             } catch (e) {
  816.                 throw Components.results.NS_NOINTERFACE;
  817.             }
  818.         },
  819.         onProgress : function (aRequest, aContext, aProgress, aProgressMax) { },
  820.         onStatus : function (aRequest, aContext, aStatus, aStatusArg) { },
  821.         onRedirect : function (aOldChannel, aNewChannel) { },
  822.         QueryInterface : function(aIID) {
  823.             if (aIID.equals(Components.interfaces.nsISupports) ||
  824.                 aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
  825.                 aIID.equals(Components.interfaces.nsIChannelEventSink) || 
  826.                 aIID.equals(Components.interfaces.nsIProgressEventSink) ||
  827.                 aIID.equals(Components.interfaces.nsIHttpEventSink) ||
  828.                 aIID.equals(Components.interfaces.nsIStreamListener))
  829.             return this;
  830.             throw Components.results.NS_NOINTERFACE;
  831.          } 
  832.      },
  833.      pingServer: function(topic){
  834.         var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  835.             .getService(Components.interfaces.nsIIOService);
  836.  
  837.         // create an nsIURI
  838.         var attrs = [];
  839.         var session = Date.now().toString(36);
  840.         attrs.push("app="       + "jezebel");
  841.         attrs.push("mid="       + gSettings.machineId);
  842.         attrs.push("sess="      + session);
  843.         attrs.push("page="      + topic);
  844.         attrs.push("username="  + gSettings.username);
  845.         attrs.push("no_cache="  + Date.now().toString(36));
  846.  
  847.         var query = attrs.join("&");
  848.         var url = gSettings.httpProtocol + "tr.xmarks.com/tracking/impressions.gif?" + query;
  849.         this._pingRequest = new Request("GET",url, null, false,null, true, true);  
  850.         this._pingRequest.Start(function(){});
  851.     },
  852.     getLastModified: function(synctype) {
  853.         return FoxmarksSyncService.lastmodified;
  854.     },
  855.  
  856.     get server() {
  857.         if (!this._server) {
  858.             this._server = new SyncServer();
  859.         }
  860.         return this._server;
  861.     },
  862.  
  863.     /////////////////////////////////////////////////////////////////////////
  864.     //
  865.     // nsIObserver
  866.  
  867.     observe: function(subject, topic, data)  { // called at startup
  868.         var timerCallback = {
  869.  
  870.             notify: function(timer) {
  871.  
  872.                 var now = new Date().getTime();
  873.  
  874.                 // scan entire bookmark set to find
  875.                 // last modified date for entire set
  876.                 // XXX: Implement
  877.  
  878.  
  879.                 // Do automatic sync if necessary
  880.                 if (!IsBusy() && (!gFailureCount || now > gBackoffUntil) &&
  881.                     gSettings.synchOnTimer && gSettings.haveSynced) {
  882.                     if (gSettings.minutesSinceLastSync > 
  883.                             gSettings.autoSynchFreq) {
  884.                         FoxmarksSyncService.synchronize(true);
  885.                     } else {
  886.                         if (FoxmarksSyncService.lastmodified && gSettings.haveSynced && 
  887.                             FoxmarksSyncService.lastmodified >
  888.                             Date.parse(gSettings.lastSynchDate) &&
  889.                             now - FoxmarksSyncService.lastmodified > 5 * 60 * 1000) {
  890.                             FoxmarksSyncService.synchronize(true);
  891.                         }
  892.                     }
  893.                 }
  894.             }
  895.         }
  896.  
  897.         if (topic == "app-startup") {
  898.             // Pre-initialization here.
  899.             var os = Cc["@mozilla.org/observer-service;1"].
  900.                 getService(Ci.nsIObserverService);
  901.             os.addObserver(this, "quit-application-requested", false);
  902.             os.addObserver(this, "foxmarks-datasourcechanged", false);
  903.             os.addObserver(this, "foxmarks-rununittest", false);
  904.             os.addObserver(this, "foxmarks-unittesterror", false);
  905.             os.addObserver(this, "foxmarks-tr", false);
  906.             os.addObserver(this, "earlyformsubmit", false);
  907.             os.addObserver(this, "final-ui-startup", false);
  908.             os.addObserver(this, "em-action-requested", false);
  909.             os.addObserver(this, "quit-application-granted", false);
  910.             os.addObserver(this, "foxmarks-showsettingpane", false);
  911.             os.addObserver(this, "foxmarks-realstart", false);
  912.         } else if (topic == "final-ui-startup") {
  913.             FoxmarksCheckForServerChanges();
  914.         } else if (topic == "foxmarks-realstart") {
  915.             // Real initialization starts here.
  916.             FoxmarksSyncService = this;
  917.             UpdateToXMarks();
  918.             var dsList = loadDatasourceSet(true); 
  919.  
  920.             FoxmarksSyncService.nat = {}; 
  921.             for(var x = 0; x < dsList.length; x++)
  922.                 FoxmarksSyncService.nat[dsList[x].syncType] = dsList[x].WatchForChanges(this.server);
  923.  
  924.             if (!gSettings.wizardSuppress && !gSettings.useOwnServer &&
  925.                     !gSettings.haveSynced) {
  926.                 LogWrite("Starting Wizard: Never Synched");
  927.                 FoxmarksLaunchSetupWizard();
  928.             } else if (gSettings.majorVersion < 2) {
  929.                 LogWrite("Starting Wizard: Major Upgrade");
  930.                 gSettings.majorVersion = 3;
  931.                 FoxmarksLaunchSetupWizard();
  932.             } else {
  933.                 // need to check for upgrades here
  934.                 gSettings.majorVersion = 3;
  935.                 var currver = FoxmarksVersion();
  936.                 var lastver = gSettings.currVersion;
  937.                 var ca = currver.split(".");
  938.                 var la = lastver.split(".");
  939.                 var newver =false;
  940.                 
  941.                 if(ca.length != la.length){
  942.                     newver = true;
  943.                 } else {
  944.                     for(var x=0; x < ca.length-1; x++){
  945.                         if(parseInt(ca[x]) != parseInt(la[x])){
  946.                             newver = true;
  947.                             break;
  948.                         }
  949.                     }
  950.                 }
  951.                 if(newver){
  952.                     FoxmarksLaunchUpgradePage();
  953.                 }
  954.             }
  955.             
  956.             this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  957.             this.timer.initWithCallback(timerCallback, 1000*60,
  958.                 Ci.nsITimer.TYPE_REPEATING_SLACK);
  959.         } else if (topic == "foxmarks-tr") {
  960.             this.pingServer(data);
  961.  
  962.         } else if (topic == "quit-application-requested") {
  963.             HandleShutdown();
  964.         } else if(topic == "em-action-requested"){
  965.             subject.QueryInterface(Components.interfaces.nsIUpdateItem);
  966.             if(subject.id == "foxmarks@kei.com"){
  967.                 switch(data){
  968.                     case 'item-uninstalled':
  969.                         this._uninstall = true;
  970.                         this.pingServer("uninstall");
  971.                         break;
  972.                     case 'item-disabled':
  973.                         this.pingServer("disable");
  974.                         break;
  975.                     case 'item-cancel-action':
  976.                         if(this._uninstall == false){
  977.                             this.pingServer("cancel-disable");
  978.                         } else {
  979.                             this.pingServer("cancel-uninstall");
  980.                         }
  981.                         this._uninstall = false;
  982.                         break;
  983.                 }
  984.  
  985.             }
  986.  
  987.         } else if(topic == "quit-application-granted"){
  988.             if(this._uninstall){
  989.                 gSettings.clearAllPrefs();
  990.             }
  991.         } else if(topic == "foxmarks-showsettingpane"){
  992.             if(data.length > 0){
  993.                 OpenFoxmarksSettingsDialog(data);
  994.             }
  995.         } else if (topic == "foxmarks-unittesterror"){
  996.             try {
  997.                 ReturnErrorCode(parseInt(data));
  998.             }
  999.             catch(e){
  1000.                 Components.utils.reportError(e);
  1001.             }
  1002.         } else if (topic == "foxmarks-rununittest"){
  1003.             this.server.runUnitTest();
  1004.         } else if (topic == "foxmarks-datasourcechanged") {
  1005.             var a = data.split(';');
  1006.             this.lastmodified = parseInt(a[0]);
  1007.             var okState = (GetState() == "ready" || GetState() == "unknown");
  1008.             if (okState && gSettings.haveSynced &&
  1009.                 gSettings.isSyncEnabled(a[1]) && 
  1010.                 this.lastmodified > Date.parse(gSettings.lastSynchDate)) {
  1011.                 SetState("dirty");
  1012.             }
  1013.         } else {
  1014.             LogWrite("Yikes unknown topic " + topic);
  1015.         }
  1016.     },
  1017.  
  1018.     /////////////////////////////////////////////////////////////////////////
  1019.     // nsIFormSubmitObserver
  1020.     notify : function (formElement, aWindow, actionURI) {
  1021.         if(FoxmarksSyncService && FoxmarksSyncService.nat["passwords"])
  1022.             FoxmarksSyncService.nat["passwords"].formsubmit(formElement);
  1023.         return true;
  1024.     },
  1025.  
  1026.     /////////////////////////////////////////////////////////////////////////
  1027.     // nsIClassInfo
  1028.     getInterfaces: function (aCount) {
  1029.         var interfaces = [Ci.nsIFoxmarksService,
  1030.         Ci.nsIObserver,
  1031.         Ci.nsIFormSubmitObserver,
  1032.         Ci.nsiRDFObserver];
  1033.         aCount.value = interfaces.length;
  1034.         return interfaces;
  1035.     },
  1036.  
  1037.     getHelperForLanguage: function (aLanguage) {
  1038.         return null;
  1039.     },
  1040.  
  1041.     get contractID() {
  1042.         return "@foxcloud.com/extensions/foxmarks;1";
  1043.     },
  1044.  
  1045.     get classDescription() {
  1046.         return "Foxmarks Service";
  1047.     },
  1048.  
  1049.     get classID() {
  1050.         return Components.ID("{49ace257-6111-48b2-a988-f9eb38b0fa58}");
  1051.     },
  1052.  
  1053.     get implementationLanguage() {
  1054.         return Ci.nsIProgrammingLanguage.JAVASCRIPT;
  1055.     },
  1056.  
  1057.     get flags() {
  1058.         return Ci.nsIClassInfo.SINGLETON;
  1059.     },
  1060.  
  1061.     /////////////////////////////////////////////////////////////////////////
  1062.     // nsISupports
  1063.     QueryInterface: function (aIID) {
  1064.         if (!aIID.equals(Ci.nsIFoxmarksService) &&
  1065.             !aIID.equals(Ci.nsISupports) &&
  1066.             !aIID.equals(Ci.nsIRDFObserver) &&
  1067.             !aIID.equals(Ci.nsIFormSubmitObserver) &&
  1068.             !aIID.equals(Ci.nsIObserver))
  1069.         throw Components.results.NS_ERROR_NO_INTERFACE;
  1070.         return this;
  1071.     }
  1072. };
  1073.  
  1074. var gModule = {
  1075.     _firstTime: true,
  1076.  
  1077.     registerSelf: function (aComponentManager, aFileSpec, aLocation, aType) {
  1078.         if (this._firstTime) {
  1079.             this._firstTime = false;
  1080.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  1081.         }
  1082.  
  1083.  
  1084.         aComponentManager = aComponentManager.
  1085.         QueryInterface(Ci.nsIComponentRegistrar);
  1086.  
  1087.         for (var key in this._objects) {
  1088.             var obj = this._objects[key];
  1089.             aComponentManager.registerFactoryLocation(obj.CID, obj.className,
  1090.                 obj.contractID, aFileSpec, aLocation, aType);
  1091.         }
  1092.  
  1093.         // Make the Foxmarks Service a startup observer
  1094.         var cm = Cc["@mozilla.org/categorymanager;1"].
  1095.         getService(Ci.nsICategoryManager);
  1096.         cm.addCategoryEntry("app-startup", this._objects.service.className,
  1097.             "service," + this._objects.service.contractID, true, true, null);
  1098.     },
  1099.  
  1100.  
  1101.     getClassObject: function (aComponentManager, aCID, aIID) {
  1102.         if (!aIID.equals(Ci.nsIFactory))
  1103.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1104.  
  1105.         for (var key in this._objects) {
  1106.             if (aCID.equals(this._objects[key].CID))
  1107.                 return this._objects[key].factory;
  1108.         }
  1109.  
  1110.         throw Components.results.NS_ERROR_NO_INTERFACE;
  1111.     },
  1112.  
  1113.     _makeFactory: #1= function(ctor) {
  1114.         return {
  1115.             createInstance: function (outer, iid) {
  1116.                 if (outer != null)
  1117.                     throw Components.results.NS_ERROR_NO_AGGREGATION;
  1118.                 return (new ctor()).QueryInterface(iid);
  1119.             }
  1120.         };
  1121.     },
  1122.  
  1123.     _objects: {
  1124.         service: { CID : nsFoxmarksService.prototype.classID,
  1125.             contractID : nsFoxmarksService.prototype.contractID,
  1126.             className  : nsFoxmarksService.prototype.classDescription,
  1127.             factory    : #1#(nsFoxmarksService)
  1128.         }
  1129.     },
  1130.  
  1131.     canUnload: function (aComponentManager)
  1132.     {
  1133.         return true;
  1134.     }
  1135. };
  1136.  
  1137. function NSGetModule(compMgr, fileSpec)
  1138. {
  1139.     return gModule;
  1140. }
  1141.